﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace CorporationWars
{
    [Serializable]
    public class ScrollableListBox<T> : UIControlBase
    {
        static Vector2 listPosOffset = new Vector2(12, 8);
        float scrollerVal = 0;

        protected Vector2 listpos { get; set; }
        protected Vector2 _Position { get; set; }
        public override Vector2 Position { get { return _Position; } set { _Position = value; listpos = _Position + listPosOffset; } }

        int _width { get; set; }
        int _height { get; set; }
        public override int Width { get { return _width; } }
        public override int Height { get { return _height; } }
        protected List<T> _Items { get; set; }
        public List<T> Items { get { return _Items; } set { _Items = value; scrollerVal = 0; SelectedItem = default(T); Text = string.Empty; DisplayItems = null; ColoredItems.Clear(); } }
        List<UILabel> DisplayItems { get; set; }
        string FontAsset { get; set; }

        public bool SaySelected = false;

        public Color ForeColor = Color.White;

        public Dictionary<T, Color> ColoredItems = new Dictionary<T, Color>();

        public bool IsVisible { get; set; }

        public int ScrollerSize { get; set; }

        Vector2 posOffset { get; set; }

        public bool NumberedLines { get; set; }
        public bool DecendingNumbers { get; set; }

        int lsp { get { return DisplayItems != null && DisplayItems.Count > 0 ? DisplayItems[0].Height : Game.Content.Load<SpriteFont>(FontAsset).LineSpacing + 1; } }
        int pageSize { get { return Height / lsp; } }
        PropertyInfo prop;

        UIZone SelectedTextBG;
        UIZone SelectedText;
        UIZone HR;

        UIZone bg1;
        UIZone bg2;

        UIZone scroller;
        UIZone scrollerMark;

        UIButton OK;
        UIButton Cancel;

        [NonSerialized]
        public UIEvent OnCancel;
        [NonSerialized]
        public UIEvent OnOK;
        [NonSerialized]
        public UIEvent OnSelectionChange;

        public T SelectedItem;

        public bool ShowSelectedText { get; set; }
        public bool Closeable { get; set; }

        public string Text;

        public ScrollableListBox(Game1 game, string renderProperty, Vector2 position, int width, int height, string fontAsset = "Fonts/fontVSmall", bool showSelectedText = true) 
            : base(game, false)
        {
            ScrollerSize = 32;

            Closeable = true;

            ShowSelectedText = showSelectedText;

            Text = string.Empty;
            Position = position;
            _width = width;
            _height = height;
            FontAsset = fontAsset;

            posOffset = Vector2.Zero;

            if (ShowSelectedText)
            {
                SelectedText = new UIZone(Game)
                {
                    //OffSet = position,
                    Width = width,
                    Height = 32,
                    TextureAsset = "Textures/textBoxBG",
                    Color = ForeColor,

                };

                AddControl(SelectedText);

                SelectedTextBG = new UIZone(Game)
                {
                    //OffSet = position,
                    Width = width,
                    Height = 32,
                    TextureAsset = "Textures/halfwhitetonothing",
                    Color = Color.White,

                };

                AddControl(SelectedTextBG);

                posOffset = new Vector2(0, 32);
            }

            HR = new UIZone(Game)
            {
                OffSet = posOffset,
                Width = width,
                Height = 1,
                TextureAsset = "Textures/halfwhite",
                Color = Color.White,

            };

            AddControl(HR);

            posOffset = new Vector2(posOffset.X, posOffset.Y + 1);

            bg1 = new UIZone(Game)
            {
                OffSet = posOffset,
                Width = width,
                Height = height,
                TextureAsset = "Textures/halfwhitetonothing",
                Color = Color.White,
                OpacityTolerance = 0,
            };

            //AddControl(bg1);

            bg2 = new UIZone(Game)
            {
                OffSet = posOffset,
                Width = width,
                Height = height,
                TextureAsset = "Textures/textBoxBG",
                Color = Color.White,
                OpacityTolerance = 0,
            };
            AddControl(bg2);

            OK = new UIButton(Game)
            {
                Name ="OK",
                OffSet = new Vector2(width - 16, height) + posOffset,
                Color = Color.White,
                Width = 16,
                Height = 16,
                MouseDownColor = Color.SteelBlue,
                HoverColor = Color.LightSteelBlue,
                //OnClick = ButtonClicked,
                TextureAsset = "Textures/HUD/Buttons/OK",
                OpacityTolerance = 0,
            };
            AddControl(OK);

            Cancel = new UIButton(Game)
            {
                Name = "Cancel",
                OffSet = new Vector2(0, height) + posOffset,
                Color = Color.White,
                Width = 16,
                Height = 16,
                MouseDownColor = Color.SteelBlue,
                HoverColor = Color.LightSteelBlue,
                //OnClick = ButtonClicked,
                TextureAsset = "Textures/HUD/Buttons/Cancel",
                OpacityTolerance = 0,
            };
            AddControl(Cancel);

            scroller = new UIZone(Game)
            {
                OffSet = new Vector2(width - 16, 0) + posOffset,
                Name = "scroller",
                Width = 16,
                Height = Height,
                TextureAsset = "Textures/halfwhite",
                Color = Color.White,
                OnClick = Scroll,
                OnClickHeld = Scroll,
                OpacityTolerance = 0,
            };
            AddControl(scroller);

            scrollerMark = new UIZone(Game)
            {
                OffSet = new Vector2(width - 16, 0) + posOffset,
                Width = 16,
                Height = 16,
                TextureAsset = "Textures/halfwhite",
                Color = Color.Cyan,
                OpacityTolerance = 0,
            };
            AddControl(scrollerMark);

            prop = typeof(T).GetProperty(renderProperty);
        }

        protected override void ButtonClicked(UIZone sender)
        {
            if (sender.Name == "OK" && OnOK != null)
                OnOK(sender);             

            if (sender.Name == "Cancel" && OnCancel != null)
                OnCancel(sender);

            IsVisible = false;
            SelectedItem = default(T);
            Text = string.Empty;
        }

        public void SetSelectedItem(UIZone sender)
        {
            string text = ((UILabel)sender).Text;

            if (NumberedLines) // Chop off the number...
                text = text.Substring(text.IndexOf("- ") + 2);

            T item = default(T);

            if (typeof(T) == typeof(string))
                item = Items.SingleOrDefault(i => i.ToString() == text);
            else
                item = Items.SingleOrDefault(i => prop.GetValue(i, null).Equals(text));

            if (item != null)
            {
                SelectedItem = item;

                if (OnSelectionChange != null)
                    OnSelectionChange(sender);

                Text = text;

                if (SaySelected)
                    Game.SayAsync(text);
            }
        }

        int scrollState = 0; 

        protected void Scroll()
        {
            scrollerVal += pageSize * scrollState; 
        }
        

        public void Scroll(UIZone sender)
        {
            
            if (DisplayItems != null && DisplayItems.Count > 0)
            {
                if (Game.ptrPos.Y < scrollerMark.Position.Y + scrollerMark.OffSet.Y)
                    scrollState = -1;

                if (Game.ptrPos.Y > scrollerMark.Position.Y + scrollerMark.OffSet.Y + scrollerMark.Height)
                    scrollState = 1;
            }

            Scroll();
        }

        float mkrPos = 0;

        public void Update(GameTime gameTime, float scrollerValue)
        {
            base.Update(gameTime);

            if (DisplayItems != null)
            {
                mkrPos = MathHelper.Lerp(scroller.OffSet.Y, scroller.OffSet.Y + scroller.Height, (float)start / DisplayItems.Count);
                scrollerMark.OffSet = new Vector2(scrollerMark.OffSet.X, mkrPos);
            }

            scrollerVal -= scrollerValue;

            if (scrollerVal < 0)
                scrollerVal = 0;

            if (Items != null)
            {
                int icnt = Items.Count;

                if (scrollerVal > icnt - 1)
                    scrollerVal = icnt - 1;
            }

            //if (ShowSelectedText)
            //{
            //    SelectedTextBG.Update(gameTime);
            //    SelectedText.Update(gameTime);
            //}

            if (Items != null)
            {
                if (DisplayItems == null)
                    DisplayItems = new List<UILabel>();

                if (DisplayItems.Count != Items.Count)
                {
                    DisplayItems.Clear();

                    int ln = 0;
                    int mod = 1;
                    if (DecendingNumbers)
                    {
                        ln = Items.Count + 1;
                        mod = -1;
                    }

                    string txt = string.Empty;

                    foreach (T item in Items)
                    {
                        if (typeof(T) == typeof(string))
                            txt = item.ToString();
                        else
                            txt = (string)prop.GetValue(item, null);
                        
                        if (NumberedLines)
                            DisplayItems.Add(new UILabel(Game, string.Format("{0:0000} - {1}", ln += mod, txt), Vector2.Zero, FontAsset, ForeColor, Color.Red, new Vector2(4, 0)) { OnClick = SetSelectedItem, Width = bg1.Width - (int)(listPosOffset.X * 1.5f) - (ScrollerSize / 2), Tag = item });
                        else
                            DisplayItems.Add(new UILabel(Game, txt, Vector2.Zero, FontAsset, ForeColor, Color.Red, new Vector2(4, 0)) { OnClick = SetSelectedItem, Width = bg1.Width - (int)(listPosOffset.X * 1.5f) - (ScrollerSize / 2), Tag = item });
                    }
                }
            }

            //bg1.Update(gameTime);
            //bg2.Update(gameTime);

            if (DisplayItems != null)
            {
                SetScrollerStartEnd();

                for (int c = start; c - (int)scrollerVal >= 0 && c - (int)scrollerVal < end; c++)
                    DisplayItems[c].Update(gameTime);
            }

            //scroller.Update(gameTime);
            //scrollerMark.Update(gameTime);

            //if (Closeable)
            //{
            //    OK.Update(gameTime);
            //    Cancel.Update(gameTime);
            //}
        }

        int start;
        int end;

        public void SetScrollerStartEnd()
        {
            if (pageSize > DisplayItems.Count)
                scrollerVal = 0;

            start = (int)scrollerVal;

            if (start < 0)
                start = 0;

            if (start > DisplayItems.Count)
                start = DisplayItems.Count - 1;

            end = pageSize;

            if (start + end > DisplayItems.Count - 1)
                end = (DisplayItems.Count) - start;
        }

        public void Draw(GameTime gameTime)
        {
            if (!IsVisible)
                return;

            //if (ShowSelectedText)
            //{
            //    SelectedTextBG.Draw(gameTime);
            //    SelectedText.Draw( gameTime);
            //}

            //HR.Draw(gameTime);
            //HR.Draw(gameTime);

            ////bg1.DrawZone(Game, gameTime);
            //bg2.Draw(gameTime);


            if (Items != null)
            {
                int dcnt = DisplayItems.Count;

                SetScrollerStartEnd();

                for (int c = start; c - (int)scrollerVal >= 0 &&  c - (int)scrollerVal < end; c++)
                {
                    T item = Items[c];

                    DisplayItems[c].Position = new Vector2(listpos.X, listpos.Y + (lsp * (c - (int)scrollerVal))) + posOffset;

                    DisplayItems[c].Color = ForeColor;

                    if (ColoredItems.ContainsKey(item))
                        DisplayItems[c].Color = ColoredItems[item];

                    if (Text == (NumberedLines ? DisplayItems[c].Text.Substring(DisplayItems[c].Text.IndexOf("- ")+2) : DisplayItems[c].Text))
                        DisplayItems[c].Selected = true;
                    else
                        DisplayItems[c].Selected = false;

                    DisplayItems[c].Draw( gameTime);
                }
            }
            
            //scroller.Draw( gameTime);

            base.Draw(gameTime);
            
            //scrollerMark.Draw(gameTime);

            //if (Closeable)
            //{
            //    OK.Draw(gameTime);
            //    Cancel.Draw(gameTime);
            //}

            if (ShowSelectedText)
            {
                Vector2 textPos = SelectedText.Position + new Vector2(8, 4);
                Game.RenderText(Text, textPos, ForeColor, -1, FontAsset);
            }
        }

        public override void ReSet()
        {
            scrollerVal = 0;
        }

        public override void ReWire(Game1 game)
        {
            base.ReWire(game);

            GetControl("scroller").OnClickHeld = Scroll;

            if (DisplayItems != null)
            {
                foreach (UILabel lable in DisplayItems)
                {
                    lable.Game = Game;
                    lable.OnClick = SetSelectedItem;
                }
            }
        }
        public override void UnWire()
        {
            base.UnWire();

            scroller.OnClickHeld = null;

            GetControl("scroller").OnClickHeld = null;

            if (DisplayItems != null)
            {
                foreach (UILabel lable in DisplayItems)
                    lable.OnClick = null;
            }
        }
    }
}
